package web

import (
	"distribkv/src/config"
	"distribkv/src/db"
	"distribkv/src/replication"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

// Server contains HTTP method handlers to be used for the database
type Server struct {
	db           *db.Database
	db_instances *config.Instances
}

// NewServer creates a new Handler Server
func NewServer(db *db.Database, insts *config.Instances) *Server {
	return &Server{
		db:           db,
		db_instances: insts,
	}
}

func (s *Server) redirect(which int, w http.ResponseWriter, r *http.Request) {
	url := "http://" + s.db_instances.Addrs[which] + r.RequestURI
	fmt.Fprintf(w, "redirecting from db instance %d to db instance %d (%q)\n", s.db_instances.CurIdx, which, url)

	resp, err := http.Get(url)
	if err != nil {
		w.WriteHeader(500) // Status Code 500, sevrer internal error
		fmt.Fprintf(w, "Error redirecting the request: %v\n", err)
		return
	}
	defer resp.Body.Close()

	io.Copy(w, resp.Body)
}

// GetHandler handles "/get" request
func (s *Server) GetHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	key := r.Form.Get("key") // get url param "key"

	inst_index := s.db_instances.Index(key)

	if inst_index != s.db_instances.CurIdx {
		s.redirect(inst_index, w, r)
		return
	}

	value, err := s.db.GetKey(key)

	fmt.Fprintf(w, "DB-Instance = %d, current instance = %d, addr = %q, Value = %q, error = %v\n", inst_index, s.db_instances.CurIdx, s.db_instances.Addrs[inst_index], value, err)
}

// SetHandler handles "/set" request
func (s *Server) SetHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	key := r.Form.Get("key")
	value := r.Form.Get("value")

	inst_index := s.db_instances.Index(key)

	if inst_index != s.db_instances.CurIdx {
		s.redirect(inst_index, w, r)
		return
	}

	err := s.db.SetKey(key, []byte(value))
	fmt.Fprintf(w, "Error = %v, instance index = %d, current instance = %d\n", err, inst_index, s.db_instances.CurIdx)
}

func (s *Server) DeleteExtraKeysHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Error = %v", s.db.DeleteExtraKeys(func(key string) bool {
		return s.db_instances.Index(key) != s.db_instances.CurIdx
	}))
}

// GetNextKeyForReplication returns the next key for replication.
func (s *Server) GetNextKeyForReplication(w http.ResponseWriter, r *http.Request) {
	enc := json.NewEncoder(w)
	k, v, err := s.db.GetNextKeyForReplication()
	enc.Encode(&replication.NextKeyValue{
		Key:   string(k),
		Value: string(v),
		Err:   err,
	})
}

// DeleteReplicationKey deletes the key from replica queue.
func (s *Server) DeleyeReplicationKey(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	key := r.Form.Get("key")
	value := r.Form.Get("value")

	err := s.db.DeleteReplicationKey([]byte(key), []byte(value))
	if err != nil {
		w.WriteHeader(http.StatusExpectationFailed)
		fmt.Fprintf(w, "error: %v", err)
		return
	}

	fmt.Fprintf(w, "ok")
}
